// User.cs
// Facebook/Framework/Service
// Copyright (c) 2007, Nikhil Kothari. All Rights Reserved.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Facebook.Service.Core;

namespace Facebook.Service
{

	/// <summary>
	/// Represents a Facebook user.
	/// </summary>
	public sealed class User : FacebookEntity
	{

		/// <summary>
		/// The list of basic user information fields such as ID and name.
		/// </summary>
		public const string BasicFields =
			"uid,first_name,last_name,name";

		/// <summary>
		/// The list of all user information fields.
		/// </summary>
		public const string AllFields =
			"uid,first_name,last_name,name," +
			"birthday,sex," +
			"pic_square,pic_small,pic_big,pic," +
			"hometown_location,current_location,timezone," +
			"meeting_sex,meeting_for,relationship_status,significant_other_id," +
			"political,activities,interests,music,tv,movies,books,quotes,about_me," +
			"affiliations,religion," +
			"hs_info,education_history,work_history," +
			"profile_update_time,status,is_app_user,has_added_app," +
			"notes_count,wall_count";

		/// <summary>
		/// The list of fields that may not be restricted by the user's privacy settings.
		/// </summary>
		/// <remarks>
		/// These will only be inaccessible if the user has completely turned off access to the Facebook platform.
		/// </remarks>
		public const string UnrestrictedFields = "uid,first_name,last_name,name,affiliations,timezone,birthday,sex";

		private bool _networksTransformed;
		private bool _currentLocationTransformed;
		private bool _hometownTransformed;
		private bool _genderTransformed;
		private bool _interestedInGendersTransformed;
		private bool _interestedInRelationshipsTransformed;
		private bool _workHistoryTransformed;
		private bool _educationHistoryTransformed;
		private bool _statusTransformed;
		private bool _idTransformed;

		internal User(Hashtable userState)
			: base(userState)
		{
		}

		/// <summary>
		/// The "About Me" section of the user's profile.
		/// </summary>
		public string About
		{
			get
			{
				return GetState<string>("about_me") ?? String.Empty;
			}
		}

		/// <summary>
		/// The Activities section of the user's profile.
		/// </summary>
		public string Activities
		{
			get
			{
				return GetState<string>("activities") ?? String.Empty;
			}
		}

		/// <summary>
		/// The user's birthday information as shared on the profile.
		/// </summary>
		public string Birthday
		{
			get
			{
				return GetState<string>("birthday") ?? String.Empty;
			}
		}

		/// <summary>
		/// The Books section of the user's profile.
		/// </summary>
		public string Books
		{
			get
			{
				return GetState<string>("books") ?? String.Empty;
			}
		}

		/// <summary>
		/// The Current Location entry on the user's profile.
		/// </summary>
		public Location CurrentLocation
		{
			get
			{
				if (_currentLocationTransformed == false)
				{
					return TransformCurrentLocation();
				}
				return (Location)this["current_location"];
			}
		}

		/// <summary>
		/// Any education history listed on the user's profile.
		/// </summary>
		public ICollection<EducationHistory> EducationHistory
		{
			get
			{
				if (_educationHistoryTransformed == false)
				{
					return TransformEducationHistory();
				}
				return (ICollection<EducationHistory>)this["education_history"];
			}
		}

		/// <summary>
		/// The user's first name.
		/// </summary>
		public string FirstName
		{
			get
			{
				return GetState<string>("first_name") ?? String.Empty;
			}
		}

		/// <summary>
		/// The user's gender as shared on the profile.
		/// </summary>
		public UserGender Gender
		{
			get
			{
				if (_genderTransformed == false)
				{
					return TransformGender();
				}
				return (UserGender)this["sex"];
			}
		}

		/// <summary>
		/// Whether the user has added the current application to their facebook profile.
		/// </summary>
		public bool HasAddedApp
		{
			get
			{
				return GetState<bool>("has_added_app");
			}
		}

		/// <summary>
		/// The user's home town as shared on the profile.
		/// </summary>
		public Location Hometown
		{
			get
			{
				if (_hometownTransformed == false)
				{
					return TransformHometown();
				}
				return (Location)this["hometown_location"];
			}
		}

		/// <summary>
		/// The unique ID of the user.
		/// </summary>
		public string ID
		{
			get
			{
				if (_idTransformed == false)
				{
					return TransformID();
				}
				return GetState<string>("uid");
			}
		}

		/// <summary>
		/// The Interests section of the user's profile.
		/// </summary>
		public string Interests
		{
			get
			{
				return GetState<string>("interests") ?? String.Empty;
			}
		}

		/// <summary>
		/// The list of desired relationship genders as shared on the user's profile.
		/// </summary>
		public ICollection<UserGender> InterestedInGenders
		{
			get
			{
				if (_interestedInGendersTransformed == false)
				{
					TransformInterestedInGenders();
				}
				return (ICollection<UserGender>)this["meeting_sex"];
			}
		}

		/// <summary>
		/// The list of desired relationship types as shared on the user's profile.
		/// </summary>
		public ICollection<string> InterestedInRelationships
		{
			get
			{
				if (_interestedInRelationshipsTransformed == false)
				{
					TransformInterestedInRelationships();
				}
				return (ICollection<string>)this["meeting_for"];
			}
		}

		/// <summary>
		/// Whether the user has used the current application.
		/// </summary>
		public bool IsAppUser
		{
			get
			{
				return GetState<bool>("is_app_user");
			}
		}

		/// <summary>
		/// The URL of the larger version of the user's profile picture.
		/// </summary>
		public string LargePictureUrl
		{
			get
			{
				return GetState<string>("pic_big") ?? String.Empty;
			}
		}

		/// <summary>
		/// The user's last name.
		/// </summary>
		public string LastName
		{
			get
			{
				return GetState<string>("last_name") ?? String.Empty;
			}
		}

		/// <summary>
		/// The Movies section of the user's profile.
		/// </summary>
		public string Movies
		{
			get
			{
				return GetState<string>("movies") ?? String.Empty;
			}
		}

		/// <summary>
		/// The Music section of the user's profile.
		/// </summary>
		public string Music
		{
			get
			{
				return GetState<string>("music") ?? String.Empty;
			}
		}

		/// <summary>
		/// The user's full name.
		/// </summary>
		public string Name
		{
			get
			{
				return GetState<string>("name") ?? String.Empty;
			}
		}

		/// <summary>
		/// The list of networks that the user is affiliated with.
		/// </summary>
		public ICollection<UserNetwork> Networks
		{
			get
			{
				if (_networksTransformed == false)
				{
					return TransformNetworks();
				}
				return (ICollection<UserNetwork>)this["affiliations"];
			}
		}

		/// <summary>
		/// The URL of the normal sized version of the user's profile picture.
		/// </summary>
		public string PictureUrl
		{
			get
			{
				return GetState<string>("pic") ?? String.Empty;
			}
		}

		/// <summary>
		/// The user's political view as shared on the profile.
		/// </summary>
		public string PoliticalView
		{
			get
			{
				return GetState<string>("political") ?? String.Empty;
			}
		}

		/// <summary>
		/// The Quotes section of the user's profile.
		/// </summary>
		public string Quotes
		{
			get
			{
				return GetState<string>("quotes") ?? String.Empty;
			}
		}

		/// <summary>
		/// The user's relationship status as shared on the profile.
		/// </summary>
		public string Relationship
		{
			get
			{
				return GetState<string>("relationship_status") ?? String.Empty;
			}
		}

		/// <summary>
		/// The user's religious views as shared on the profile.
		/// </summary>
		public string Religion
		{
			get
			{
				return GetState<string>("religion") ?? String.Empty;
			}
		}

		/// <summary>
		/// The ID of the user related to the user in their relationship status.
		/// </summary>
		public string RelatedUserID
		{
			get
			{
				return GetState<string>("significant_other_id") ?? String.Empty;
			}
		}

		/// <summary>
		/// The URL of the small version of the user's profile picture.
		/// </summary>
		public string SmallPictureUrl
		{
			get
			{
				return GetState<string>("pic_small") ?? String.Empty;
			}
		}

		/// <summary>
		/// The URL of the square section of the user's profile picture.
		/// </summary>
		public string SquarePictureUrl
		{
			get
			{
				return GetState<string>("pic_square") ?? String.Empty;
			}
		}

		/// <summary>
		/// The latest status associated with the user.
		/// </summary>
		public UserStatus Status
		{
			get
			{
				if (_statusTransformed == false)
				{
					return TransformStatus();
				}
				return (UserStatus)this["status"];
			}
		}

		/// <summary>
		/// The user's time zone offset.
		/// </summary>
		public int TimeZoneOffset
		{
			get
			{
				return GetState<int>("timezone");
			}
		}

		/// <summary>
		/// The TV Shows section of the user's profile.
		/// </summary>
		public string TVShows
		{
			get
			{
				return GetState<string>("tv") ?? String.Empty;
			}
		}

		/// <summary>
		/// The number of wall posts on the user's profile.
		/// </summary>
		public int WallPosts
		{
			get
			{
				return GetState<int>("wall_count");
			}
		}

		/// <summary>
		/// Any work history listed on the user's profile.
		/// </summary>
		public ICollection<WorkHistory> WorkHistory
		{
			get
			{
				if (_workHistoryTransformed == false)
				{
					return TransformWorkHistory();
				}
				return (ICollection<WorkHistory>)this["work_history"];
			}
		}

		private Location TransformCurrentLocation()
		{
			Debug.Assert(_currentLocationTransformed == false);

			Location currentLocation = null;

			Hashtable locationState = (Hashtable)this["current_location"];
			if (locationState != null)
			{
				currentLocation = new Location(locationState);
			}

			this["current_location"] = currentLocation;
			_currentLocationTransformed = true;

			return currentLocation;
		}

		private ICollection<EducationHistory> TransformEducationHistory()
		{
			Debug.Assert(_educationHistoryTransformed == false);

			List<EducationHistory> history = null;

			object data = this["education_history"];
			if (data is ArrayList)
			{
				ArrayList historyList = (ArrayList)data;
				int count = historyList.Count;

				history = new List<EducationHistory>(count);

				for (int i = 0; i < count; i++)
				{
					Debug.Assert(historyList[i] is Hashtable);
					history.Add(new EducationHistory((Hashtable)historyList[i]));
				}
			}
			else if (data is Hashtable)
			{
				history = new List<EducationHistory>(1);
				history.Add(new EducationHistory((Hashtable)data));
			}
			else
			{
				history = new List<EducationHistory>(0);
			}

			this["education_history"] = history;
			_educationHistoryTransformed = true;

			return history;
		}

		private UserGender TransformGender()
		{
			Debug.Assert(_genderTransformed == false);

			UserGender gender = UserGender.NotSpecified;
			string state = GetState<string>("sex") ?? String.Empty;

			if (state.Length != 0)
			{
				if (state.Equals("male"))
				{
					gender = UserGender.Male;
				}
				else if (state.Equals("female"))
				{
					gender = UserGender.Female;
				}
			}

			this["sex"] = gender;
			_genderTransformed = true;

			return gender;
		}

		private Location TransformHometown()
		{
			Debug.Assert(_hometownTransformed == false);

			Location hometown = null;

			Hashtable locationState = (Hashtable)this["hometown_location"];
			if (locationState != null)
			{
				hometown = new Location(locationState);
			}

			this["hometown_location"] = hometown;
			_hometownTransformed = true;

			return hometown;
		}

		private string TransformID()
		{
			Debug.Assert(_idTransformed == false);

			string id = FacebookResponse.CoerceString(this["uid"]);

			this["uid"] = id;
			_idTransformed = true;

			return id;
		}

		private ICollection<UserGender> TransformInterestedInGenders()
		{
			Debug.Assert(_interestedInGendersTransformed == false);

			ArrayList state = this["meeting_sex"] as ArrayList;
			List<UserGender> genders = new List<UserGender>();

			if (state != null)
			{
				for (int i = 0; i < state.Count; i++)
				{
					if (state[i].Equals("male"))
					{
						genders.Add(UserGender.Male);
					}
					else if (state[i].Equals("female"))
					{
						genders.Add(UserGender.Female);
					}
				}
			}

			this["meeting_sex"] = genders;
			_interestedInGendersTransformed = true;

			return genders;
		}

		private ICollection<string> TransformInterestedInRelationships()
		{
			Debug.Assert(_interestedInRelationshipsTransformed == false);

			ArrayList state = this["meeting_for"] as ArrayList;
			ICollection<string> relationships = null;

			if (state != null)
			{
				relationships = (string[])state.ToArray(typeof(string));
			}
			else
			{
				relationships = new string[0];
			}

			this["meeting_for"] = relationships;
			_interestedInRelationshipsTransformed = true;

			return relationships;
		}

		private ICollection<UserNetwork> TransformNetworks()
		{
			Debug.Assert(_networksTransformed == false);

			ArrayList affiliations = this["affiliations"] as ArrayList;
			int count = (affiliations != null) ? affiliations.Count : 0;

			List<UserNetwork> networks = new List<UserNetwork>(count);

			for (int i = 0; i < count; i++)
			{
				Debug.Assert(affiliations[i] is Hashtable);
				networks.Add(new UserNetwork((Hashtable)affiliations[i]));
			}

			this["affiliations"] = networks;
			_networksTransformed = true;

			return networks;
		}

		private UserStatus TransformStatus()
		{
			Debug.Assert(_statusTransformed == false);

			UserStatus status = null;

			Hashtable statusState = (Hashtable)this["status"];
			if (statusState != null)
			{
				status = new UserStatus(statusState);
			}

			this["status"] = status;
			_statusTransformed = true;

			return status;
		}

		private ICollection<WorkHistory> TransformWorkHistory()
		{
			Debug.Assert(_workHistoryTransformed == false);

			List<WorkHistory> history = null;

			object data = this["work_history"];
			if (data is ArrayList)
			{
				ArrayList historyList = (ArrayList)data;
				int count = historyList.Count;

				history = new List<WorkHistory>(count);

				for (int i = 0; i < count; i++)
				{
					Debug.Assert(historyList[i] is Hashtable);
					history.Add(new WorkHistory((Hashtable)historyList[i]));
				}
			}
			else if (data is Hashtable)
			{
				history = new List<WorkHistory>(1);
				history.Add(new WorkHistory((Hashtable)data));
			}
			else
			{
				history = new List<WorkHistory>(0);
			}

			this["work_history"] = history;
			_workHistoryTransformed = true;

			return history;
		}
	}
}
